using Engine;

namespace RuthlessConquest {
    public class BodyGrid {
        public Point2 GridCount;

        public DynamicArray<Body>[] Cells;

        public int QueryIndex;

        public readonly Point2 GridSize;

        public readonly int GridCellSize;

        public BodyGrid(Point2 gridSize, int gridCellSize) {
            GridSize = gridSize;
            GridCellSize = gridCellSize;
            GridCount = GridSize / GridCellSize;
            Cells = new DynamicArray<Body>[GridCount.X * GridCount.Y];
            for (int i = 0; i < Cells.Length; i++) {
                Cells[i] = new DynamicArray<Body>();
            }
        }

        public void Add(Body body) {
            Rectangle rectangle = CalculateRectangle(body.Position, body.GridRadius);
            for (int i = rectangle.Top; i < rectangle.Bottom; i++) {
                for (int j = rectangle.Left; j < rectangle.Right; j++) {
                    GetGridCell(new Point2(j, i)).Add(body);
                }
            }
        }

        public void Clear() {
            DynamicArray<Body>[] cells = Cells;
            for (int i = 0; i < cells.Length; i++) {
                cells[i].Clear();
            }
        }

        public void QueryBodies(Point2 position, int radius, DynamicArray<Body> result, int maxCount) {
            Rectangle rectangle = CalculateRectangle(position, radius);
            int num = 0;
            for (int i = rectangle.Top; i < rectangle.Bottom; i++) {
                for (int j = rectangle.Left; j < rectangle.Right; j++) {
                    foreach (Body item in GetGridCell(new Point2(j, i))) {
                        if (num >= maxCount) {
                            break;
                        }
                        if (item.GridQueryIndex != QueryIndex
                            && IntMath.ApproxDistance(item.Position, position) < radius + item.GridRadius) {
                            result.Add(item);
                            item.GridQueryIndex = QueryIndex;
                            num++;
                        }
                    }
                }
            }
            QueryIndex++;
        }

        public Rectangle CalculateRectangle(Point2 position, int radius) {
            int num = MathUtils.Max((position.X - radius) / GridCellSize, 0);
            int num2 = MathUtils.Max((position.Y - radius) / GridCellSize, 0);
            int num3 = MathUtils.Min((position.X + radius) / GridCellSize, GridCount.X - 1);
            int num4 = MathUtils.Min((position.Y + radius) / GridCellSize, GridCount.Y - 1);
            return new Rectangle(num, num2, num3 - num + 1, num4 - num2 + 1);
        }

        public DynamicArray<Body> GetGridCell(Point2 p) => Cells[p.X + p.Y * GridCount.X];
    }
}